feat(connector-mcp): policy-gate declarative stdio transports — default-deny + host allowlist (#3055)#3059
Merged
Merged
Conversation
…e upstream (#3017) ADR-0097 made every declarative-connector materialization failure fatal at boot — right for configuration faults, wrong for operational ones: a provider: 'mcp' instance must contact its MCP server (tools/list) to materialize, so a transient network blip aborted the whole app boot. - spec: ConnectorUpstreamUnavailableError (code CONNECTOR_UPSTREAM_UNAVAILABLE, structural guard isConnectorUpstreamUnavailable) lets a provider factory mark a failure as 'upstream temporarily unreachable — degrade and retry' instead of fatal. New integration/connector-provider-errors.ts; api-surface +3. - service-automation: the reconcile degrades such instances in BOTH modes. With no live connector under the name it registers an action-less husk — state: 'degraded' + degradedReason on the GET /connectors descriptor, status: 'error' on the def — so the instance stays visible instead of silently missing; connector_action dispatch fails with the reason and a 'retries automatically' pointer. On a changed-config re-materialization the old connector keeps serving. Degraded instances retry on an exponential backoff (5s doubling to 5min, reset by config edits) and on every metadata:reloaded reconcile; recovery swaps the husk atomically. Reconcile runs (boot / reload / retry timer) are serialized; destroy() cancels the retry loop. - connector-mcp: connect / tools/list failures are classified unavailable; transport-shape validation stays a plain (fatal) throw. Configuration faults (unknown provider, invalid providerConfig, unresolvable credentialRef, name conflicts) keep the ADR-0097 fail-loud contract, verified by the existing tests. ADR-0097 gains an 'Upstream availability' section; the deferred live-mcp showcase demo and the openapi remote-URL classification are recorded as scope boundaries. Tests: 10 new reconcile cases (degrade visibility, pointed dispatch error, fake-timer recovery, exponential backoff, reload retry, removal while degraded, changed-config keeps old serving, revert cancels retry, shutdown cancels retry, plain-throw still fatal); 3 new mcp-provider classification cases; 3 new spec error-contract cases. Refs #3017 (mechanism; showcase demo deferred — see ADR scope boundaries) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pbmw3pMqNJfPbkvwh9Fkjs
…lt-deny + host allowlist (#3055) A declarative provider: 'mcp' entry with a stdio transport spawns a local child process from METADATA — which a runtime Studio publish can introduce (the reload reconcile materializes new instances; soft mode skips failures, not successes). Anyone who can publish metadata could otherwise execute arbitrary commands on the server. Declarative stdio is now denied by default and opt-in per host: new ConnectorMcpPlugin({ declarativeStdio: ['my-mcp-server'] }) // allowlist new ConnectorMcpPlugin({ declarativeStdio: true }) // full trust - createMcpProviderFactory gains a declarativeStdio policy (undefined/false = deny, string[] = strict-equality command allowlist, true = allow any); ConnectorMcpPlugin plumbs it through. New export McpDeclarativeStdioPolicy. - A violation is a CONFIGURATION fault: plain throw with an actionable opt-in message (fatal at boot, skipped+logged on reload) — never classified CONNECTOR_UPSTREAM_UNAVAILABLE, so a security rejection cannot be retried into existence (#3049 seam). - http transports are unaffected (same trust class as the http node), and so are hand-wired connectors (plugin instance options / createMcpConnector) — their command lives in host code, a different trust anchor than metadata. - ADR-0097 gains a 'Declarative stdio policy' section (cross-ref ADR-0024 §4, honest about the allowlist being a coarse boundary); this gate is the precondition for shipping connector-mcp in default presets (#3056). Tests: default-deny (plain, non-retryable, rejected before any connection attempt), allowlist hit/miss, true passthrough, http unaffected, plugin option plumb-through, hand-wired stdio path explicitly un-gated (e2e). Refs #3055. Stacked on #3049 (claude/adr-0097-mcp-nonfatal-boot). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pbmw3pMqNJfPbkvwh9Fkjs
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 1 changed package(s). ✅ |
#3049's squash (4f8c2d1) is content-identical to this branch's base commit, so this merge only rebases the PR surface — the three-dot diff vs main now shows the #3055 gate alone. # Conflicts: # docs/adr/0097-declarative-connector-instances.md # packages/connectors/connector-mcp/src/mcp-provider.test.ts # packages/connectors/connector-mcp/src/mcp-provider.ts
os-zhuang
marked this pull request as ready for review
July 16, 2026 13:31
This was referenced Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
实现 #3055(ADR-0097 安全后续):声明式
provider: 'mcp'实例的 stdio 传输默认拒绝,宿主按命令允许清单显式 opt-in——这是把connector-mcp收编进默认预设(#3056)的前置安全闸。威胁模型
声明式条目经由元数据到达——包括运行期的 Studio publish(
metadata:reloadedreconcile 会 materialize 新实例;soft 模式跳过的是失败,不是成功)。而 stdio 传输的 materialization 会 spawn 本地子进程(StdioClientTransport)。两者相乘:能发布元数据 ≈ 能在服务器上执行任意命令。今天的"屏障"只是宿主恰好没装 connector-mcp——#3056 一旦落地这道屏障就消失,必须换成显式策略。变更
createMcpProviderFactory新增declarativeStdio策略(默认拒绝),ConnectorMcpPlugin透传:CONNECTOR_UPSTREAM_UNAVAILABLE(feat(connectors): degrade + retry declarative instances on unreachable upstream (#3017) #3049 的分类接缝)——安全拒绝不能被重试"重试出来"。拒绝发生在任何连接尝试之前(测试有断言)。http传输不受影响(与http节点同一信任等级)。createMcpConnector):命令写在宿主代码里,信任锚不同——既有 e2e(手写 stdio、无策略)原样通过,并加注释作为回归绊线。McpDeclarativeStdioPolicy;ADR-0097 新增 §"Declarative stdio policy"(交叉引用 ADR-0024 §4;如实说明允许清单是粗粒度边界——放行npx≈ 放行它能跑的任意包,应列出具体信任的 server 二进制;沙箱化执行仍属企业层)。测试(23/23 通过,+6 新增)
true放行任意命令;http在默认拒绝策略下照常 materialize;既有 stdio 用例改为显式
declarativeStdioopt-in(行为变更的如实体现);changeset(minor,标注行为变更)。Closes #3055。Refs #3049 / #3056 / ADR-0097 / ADR-0024 §4。
🤖 Generated with Claude Code
https://claude.ai/code/session_01Pbmw3pMqNJfPbkvwh9Fkjs
Generated by Claude Code